﻿using FM.Unit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx;
using Styx.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.Common;
using Styx.Common.Helpers;
using Styx.TreeSharp;
using CommonBehaviors.Actions;
using Action = Styx.TreeSharp.Action;

namespace FM
{
    class ShiroFM : CombatRoutine
    {
        public static readonly Version Version = new Version(1, 1, 1);
        public override string Name { get { return "FM " + Version + ""; } }
        public override WoWClass Class { get { return WoWClass.Mage; } }
        private static LocalPlayer Me { get { return StyxWoW.Me; } }

        public override bool WantButton { get { return true; } }
        public override void OnButtonPress() { FM_GUI GUI = new FM_GUI(); GUI.ShowDialog(); }

        // Mage Bomb
        private static string MageBomb
        {
            get
            {
                if (SpellManager.HasSpell("Nether Tempest")) return "Nether Tempest";
                if (SpellManager.HasSpell("Living Bomb")) return "Living Bomb";
                else return "Frost Bomb";
            }
        }

        // Mage Tier 90 Talent
        private static string _T90
        {
            get
            {
                if (SpellManager.HasSpell("Rune of Power")) return "Rune of Power";
                if (SpellManager.HasSpell("Incanter's Ward")) return "Incanter's Ward";
                else return "Invocation";
            }
        }

        public int MultiCastMacroMT = 0;
        public int MultiCastMacroFT = 0;
        public string WhatSpell;
        public WoWSpell _Spell;

        public int AE_Min = FM.FM_Settings.Instance.Min_AE;
        public int AE_Range = 2 * (int)SpellManager.Spells["Arcane Explosion"].MaxRange;

        public override void Initialize()
        {
            Logging.Write(" ");
            Logging.Write(" ");
            Logging.Write("FM " + Version + " - By Trialbuddy");
            Logging.Write("Thanks for code inspiration to lathrodectus");
            Logging.Write("the best CC writer for Arcane Mage I've seen");
            Logging.Write(" ");
            Logging.Write(" ");
        }

        public override Composite CombatBehavior
        {
            get
            {
                return new PrioritySelector(

                    new Decorator(ret => Me.HasAura("Invisibility") || Me.HasAura("Greater Invisibility"),
                        new Sequence(
                            new Action(ret => { Logging.Write("Invisibility Active, Waiting for Combat"); Me.ClearTarget(); })
                            )),

                    new Decorator(ret => FM_Settings.Instance.Use_CD == 0,
                        new PrioritySelector(
                            new Decorator(ret => Me.CurrentTarget.IsBoss && SpellManager.Spells["Alter Time"].CooldownTimeLeft.TotalMilliseconds > 5000 || Me.HasAura("Alter Time"),
                                PyroProcControl()),
                                new Decorator(ret => !Me.CurrentTarget.IsBoss,
                                    PyroProcControl()))),

                    new Decorator(ret => FM_Settings.Instance.Use_CD == 0 && Me.CurrentTarget.IsBoss,
                        new PrioritySelector(
                            new Decorator(ret => _T90 == "Invocation",
                                CreateCooldowns()))),

                    new Decorator(ret => FM_Settings.Instance.Use_CD == 1,
                        new PrioritySelector(
                            new Decorator(ret => _T90 == "Invocation",
                                CreateCooldowns()))),

                    new Action(ret => { UseGloves(); return RunStatus.Failure; }),

                    new Decorator(ret => !Me.CurrentTarget.HasAura(MageBomb) && Me.CurrentTarget.Name != "Celestial Protector",
                        Cast(MageBomb)),

                    new Decorator(ret => MacroCheck(),
                        CreateMacro()),

                    new Decorator(ret => !Me.CurrentTarget.HasAura("Combustion") && GetTargetIgniteStrength() >= 50000,
                        CombuControl()),

                    new Decorator(ret => Me.IsMoving,
                        CreateMovement()),

                    new Decorator(ret => FM_Settings.Instance.Use_AoE,
                        CreateAoE()),

                    new Decorator(ret => _T90 == "Invocation",
                        InvoControl()),

                    new Decorator(ret => FM_Settings.Instance.Use_CD == 1,

                    new Decorator(ret => SpellManager.Spells["Alter Time"].CooldownTimeLeft.TotalMilliseconds > 5000 || Me.HasAura("Alter Time"),
                        PyroProcControl())),

                    new Decorator(ret => FM_Settings.Instance.Use_CD == 2,
                        PyroProcControl()),

                    new Decorator(ret => Me.HasAura("Heating Up") && !Me.HasAura("Pyroblast!"),
                        HeatingUpControl()),

                        Cast("Fireball")
                        );
            }
        }

        public override Composite PreCombatBuffBehavior
        {
            get
            {
                return new Decorator(ret => !Me.IsOnTransport && !Me.IsDead && !Me.IsFlying && !Me.OnTaxi && !Me.Mounted && !Me.Combat && !Me.HasAura("Food") && !Me.HasAura("Drink"),
                    new PrioritySelector(
                        new Decorator(ret => Me.HasAura("Invisibility") || Me.HasAura("Greater Invisibility"),
                        new Sequence(
                            new Action(ret => { Logging.Write("Invisibility Active, Waiting for Combat"); Me.ClearTarget(); })
                            )),
                        new Decorator(ret => MacroCheck(),
                            CreateMacro()),
                        Cast("Arcane Brilliance", ret => !Me.HasAura("Arcane Brilliance")),
                        Cast("Molten Armor", ret => !Me.HasAura("Molten Armor")),
                        RunMacroText("/cast Conjure Mana Gem", ret => !HaveManaGem() && !Me.IsCasting)
                ));
            }
        }

        public override Composite CombatBuffBehavior
        {
            get
            {
                return new Decorator(ret => Me.Combat && !Me.HasAura("Arcane Brilliance"),

                    new PrioritySelector(

                        new Decorator(ret => !Me.GotTarget && MacroCheck(),
                            CreateMacro()),

                        /*new Decorator(ret => Me.Combat && !Me.HasAura("Invoker's Energy") && Me.GetAuraByName("Invoker's Energy").TimeLeft.TotalMilliseconds < 1900,
                            Cast("Evocation"),*/

                        new Decorator(ret => Me.HasAura("Invisibility") || Me.HasAura("Greater Invisibility"),
                            new Sequence(
                                new Action(ret => { Logging.Write("Invisibility Active, Waiting for Combat"); Me.ClearTarget(); }))),
                                Cast("Arcane Brilliance", ret => !Me.HasAura("Arcane Brilliance")))
                                );
            }
        }

        Composite CreateMacro()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                return new PrioritySelector(
                    new Decorator(ret => MultiCastMacroMT > 1 || MultiCastMacroFT > 1,
                        new Decorator(ret => Me.IsCasting || Me.IsChanneling, new Action(ret => SpellManager.StopCasting()))),

                    new Decorator(ret => !SpellManager.GlobalCooldown && MultiCastMacroMT > 0,
                        new Sequence(
                            new Action(ret => SpellManager.Cast(WhatSpell)),
                    //new Action(ret => Lua.DoString("RunMacroText(\"/cast " + WhatSpell + "\")")),
                            new Action(ret => ResetAllMacros()))),

                    new Decorator(ret => !SpellManager.GlobalCooldown && MultiCastMacroFT > 0,
                        new Sequence(
                            new Action(ret => SpellManager.Cast(WhatSpell, Me.FocusedUnit)),
                    //new Action(ret => Lua.DoString("RunMacroText(\"/cast " + WhatSpell + "\")")),
                            new Action(ret => ResetAllMacros())))
                            );
            }
        }

        Composite CombuControl()
        {
            return new PrioritySelector(
                new Decorator(ret => !SpellManager.Spells["Combustion"].Cooldown,
                Cast("Combustion"))
            );
        } 

        Composite CreateMovement()
        {
            return new PrioritySelector(ret => Me.IsMoving,
                RunMacroText("/Cancelaura Alter Time", ret => Me.IsMoving && Me.HasAura("Alter Time") && Me.GetAuraByName("Alter Time").TimeLeft.TotalMilliseconds < 200),
                WalkCast("Scorch", ret => !SpellManager.Spells["Scorch"].Cooldown),
                WalkCast("Pyroblast", ret => Me.HasAura("Pyroblast!")),
                WalkCast("Inferno Blast", ret => Me.HasAura("Heating Up")),
                WalkCast(MageBomb, ret => !Me.CurrentTarget.HasAura(MageBomb) && Me.CurrentTarget.Name != "Celestial Protector"),
                WalkCast(MageBomb, ret => Me.CurrentTarget.HasAura(MageBomb) && Me.CurrentTarget.GetAuraByName(MageBomb).TimeLeft.TotalMilliseconds < 2000),

                new Decorator(ret => FM_Settings.Instance.Use_AE,
                        WalkCast("Arcane Explosion", ret => Unit.Unit.countEnemiesInRange(Me.Location, 15) >= AE_Min))

                );
        }

        Composite CreateCooldowns()
        {
            return new PrioritySelector(
                Cast("Mirror Image", ret => !SpellManager.Spells["Mirror Image"].Cooldown),
                new Decorator(ret => Me.HasAura("Pyroblast!") && Me.HasAura("Heating Up"),
                    new PrioritySelector(
                        new Action(ret => { UseTrinkets(); return RunStatus.Failure; }),
                        new Action(ret => { UseGloves(); return RunStatus.Failure; }),
                        Cast("Blood Fury", ret => Me.Race == WoWRace.Orc && !SpellManager.Spells["Blood Fury"].Cooldown),
                        Cast("Berserking", ret => Me.Race == WoWRace.Troll && !SpellManager.Spells["Berserking"].Cooldown),
                        Cast("Presence of Mind", ret => !Me.HasAura("Presence of Mind") && !SpellManager.Spells["Presence of Mind"].Cooldown),
                        UseManaGem(ret => HaveManaGem() && Me.ManaPercent < 80),
                        Cast("Alter Time", ret => !Me.HasAura("Alter Time") && !SpellManager.Spells["Alter Time"].Cooldown)
                        ))
                );
        }

        Composite CreateAoE()
        {
            return new PrioritySelector(
                new Decorator(ret => FM_Settings.Instance.Use_AE,
                        Cast("Arcane Explosion", ret => Unit.Unit.countEnemiesInRange(Me.Location, 15) >= AE_Min)
                        ));
        }

        Composite InvoControl()
        {
            return new PrioritySelector(
                new Decorator(ret => !Me.HasAura("Invoker's Energy"),
                        Cast("Evocation")
                        ));
        }

        Composite PyroProcControl()
        {
            return new PrioritySelector(
                new Decorator(ret => Me.HasAura("Pyroblast!") && Me.HasAura("Heating Up"),
                    Cast("Pyroblast"))
                //Cast("Fireball")
                    );
        }

        Composite HeatingUpControl()
        {
            return new PrioritySelector(
                new Decorator(ret => SpellManager.CanCast("Inferno Blast"),
                    Cast("Inferno Blast")
                    ));
        }

        public bool HaveManaGem()
        {
            return ObjectManager.GetObjectsOfType<WoWItem>(false).Any(item => item.Entry == 36799 || item.Entry == 81901);
        }

        //implement cooldown check!!!
        public bool MacroCheck()
        {
            using (StyxWoW.Memory.AcquireFrame())
            {
                MultiCastMacroMT = Lua.GetReturnVal<int>("return MultiCastMT", 0);
                MultiCastMacroFT = Lua.GetReturnVal<int>("return MultiCastFT", 0);
                WhatSpell = Lua.GetReturnVal<String>("return SpellName", 0);
                if (MultiCastMacroMT > 0 || MultiCastMacroFT > 0)
                {
                    if (WhatSpell == null || Me.IsDead)
                    {
                        ResetAllMacros();
                        return false;
                    }
                    else
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        private static bool CanUseEquippedItem(WoWItem item)
        {
            // Check for engineering tinkers!
            string itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell))
                return false;


            return item.Usable && item.Cooldown <= 0;
        }

        public int GetTargetIgniteStrength()
        {
            string command = "return select(15,UnitDebuff(\"target\", \"Ignite\", nil, \"PLAYER\"))";
            int baseDmg = Lua.GetReturnVal<int>(command, 0);
            return baseDmg;
        }

        public void ResetMacro(string Which)
        {
            Lua.DoString(Which + " = 0;");
        }

        public void ResetAllMacros()
        {
            ResetMacro("MultiCastMT");
            ResetMacro("MultiCastFT");
        }

        void UseTrinkets()
        {
            var firstTrinket = StyxWoW.Me.Inventory.Equipped.Trinket1;
            var secondTrinket = StyxWoW.Me.Inventory.Equipped.Trinket2;

            if (firstTrinket != null && CanUseEquippedItem(firstTrinket))
                firstTrinket.Use();

            if (secondTrinket != null && CanUseEquippedItem(secondTrinket))
                secondTrinket.Use();
        }

        void UseGloves()
        {
            var engigloves = StyxWoW.Me.Inventory.Equipped.Hands;
            if (engigloves != null && CanUseEquippedItem(engigloves))
                engigloves.Use();
        }


        private delegate T Selection<out T>(object context);

        Composite Cast(string spell, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell),
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spell))
                        ));
        }

        Composite WalkCast(string spell, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null) && !Me.IsCasting),
                    new Sequence(
                        new Action(ret => SpellManager.Cast(spell))
                    ));
        }

        public static Composite UseBagItem(string name, CanRunDecoratorDelegate cond)
        {
            WoWItem item = null;
            return new Decorator(
                delegate(object a)
                {
                    if (!cond(a))
                        return false;
                    item = Me.BagItems.FirstOrDefault(x => x.Name == name && x.Usable && x.Cooldown <= 0);
                    return item != null;
                },
            new Sequence(
                new Action(a => item.UseContainerItem())));
        }

        public static Composite UseManaGem(CanRunDecoratorDelegate cond)
        {
            WoWItem item = null;
            return new Decorator(
                delegate(object a)
                {
                    if (!cond(a))
                        return false;
                    //item = Me.BagItems.FirstOrDefault(x => (x.Name == "Mane Gem" || x.Name == "Brilliant Mana Gem") && x.Usable && x.Cooldown <= 0);
                    item = Me.Inventory.PhysicalItems.FirstOrDefault(i => i.Entry == 81901 || i.Entry == 36799);
                    return item != null;
                },
            new Sequence(
                new Action(a => item.UseContainerItem())));
        }

        Composite RunMacroText(string macro, CanRunDecoratorDelegate cond)
        {
            return new Decorator(
                       cond,
                       new Sequence(
                           new Action(a => Lua.DoString("RunMacroText(\"" + RealLuaEscape(macro) + "\")"))));
        }

        public static string RealLuaEscape(string luastring)
        {
            var bytes = Encoding.UTF8.GetBytes(luastring);
            return bytes.Aggregate(String.Empty, (current, b) => current + ("\\" + b));
        }
    }
}
